﻿/*
   Copyright 2011 Softisis

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
*/

using System.Collections.Generic;
using FOM.Impl;
using FOM.Impl.Converters;
using FOM.Interfaces;
using NUnit.Framework;

namespace TestUnits
{
    /// <summary>
    /// Because root behaviours are slighty different from the "standard" cases,
    /// we have to write tests for root mappings.
    /// </summary>
    /// <remarks>Copyright Softisis 2011 (France)</remarks>
    [TestFixture]
    public class RootMappingsTests
    {


        [SetUp]
        public void SetUp()
        {
        }

        /// <summary>
        /// Test root as is. The target object is returned as is.
        /// </summary>
        [Test]
        public void TestRootIsoReturn()
        {
            // create manager
            IManager mngr = new ManagerImpl();
            
            // register new mapper
            IMappingNode<ObjectA, ObjectA> rootmap= mngr.RegisterMapping<ObjectA>("test");

            // source object
            var srcA = TestObjectsHolder.GetObjectA();
            
            // dest object
            ObjectA tofill=new ObjectA();
            tofill.Chaine = "pouet";

            ObjectA res=(ObjectA)mngr.ApplyMapping("test", srcA, tofill);
            Assert.IsNotNull(res);
            Assert.IsTrue(tofill.Chaine.Equals(res.Chaine));
            Assert.IsTrue(object.ReferenceEquals(res, tofill));

            // stupide root cases, but to be validated: The root is a string or a value type
            // register new mapper
            IMappingNode<int, int> vtpam = mngr.RegisterMapping<int>("testvt");
            var res1 = (int)mngr.ApplyMapping("testvt", 10, 20); // int case
            Assert.IsTrue(res1.Equals(10)); // the return is an int with tyhe same value as the source

            IMappingNode<string, string> strpam = mngr.RegisterMapping<string>("teststr");
            const string basetest = "aaa";
            var res2 = (string)mngr.ApplyMapping("teststr", basetest, "bb"); // string case

            Assert.IsNotNull(res2);
            Assert.IsTrue(res2.Equals(basetest));
            Assert.IsTrue(object.ReferenceEquals(basetest, res2));// the return is the same string
        }

        /// <summary>
        /// Test root: We map root object, with new instance. So valu types are ... value type returned,
        /// and object have a new object instanciated (event for string).
        /// </summary>
        [Test]
        public void TestRootNewInstance()
        {
            // create manager
            IManager mngr = new ManagerImpl();

            // register new mapper
            IMappingNode<ObjectA, ObjectA> rootmap = mngr.RegisterMapping<ObjectA>("test");
            
            // here
            rootmap.NewInstance();

            // src obj
            var srcA = TestObjectsHolder.GetObjectA();

            // target obj. Will be ignored du to newInstance()
            ObjectA tofill = new ObjectA();
            tofill.Chaine = "pouet";

            ObjectA res = (ObjectA)mngr.ApplyMapping("test", srcA, tofill);
            Assert.IsNotNull(res);
            Assert.IsTrue(!tofill.Chaine.Equals(res.Chaine));
            Assert.IsFalse(object.ReferenceEquals(tofill, res));// the return is NOT the same object

            // stupide root cases, but to be validated: The root is a string or a value type
            // register new mapper
            IMappingNode<int, int> vtpam = mngr.RegisterMapping<int>("testvt").NewInstance();
            var res1 = (int)mngr.ApplyMapping("testvt", 10, 20); // int case
            Assert.IsTrue(res1.Equals(10)); // the return is an int with tyhe same value as the source

            IMappingNode<string, string> strpam = mngr.RegisterMapping<string>("teststr").NewInstance();
            const string basetest = "aaa";
            var res2 = (string)mngr.ApplyMapping("teststr", basetest, "bb"); // string case
            Assert.IsNotNull(res2);
            Assert.IsTrue(res2.Equals(basetest));
            Assert.IsFalse(object.ReferenceEquals(basetest, res2));// the return is NOT the same string

        }

        /// <summary>
        /// As the logic is push to the end, stupid cases must be "logic" as well.
        /// This one (not so stupid), use a converter (to have a something anyway),
        /// takes a string an return a double.
        /// </summary>
        [Test]
        public void TestRootConverterOnStupidCase()
        {
            // create manager
            IManager mngr = new ManagerImpl();

            // YES, we map a string to an double
            mngr.RegisterMapping<string, double>("test").UseConverter(new StringToValueTypesConv());

            const string source = "10,2";
            double res = (double)mngr.ApplyMapping("test", source, null);

            Assert.IsTrue(res == 10.2); // converted
        }

        /// <summary>
        /// Like the test above. But on complex object
        /// </summary>
        [Test]
        public void TestRootConverterBetween2Objects()
        {
            // create manager
            IManager mngr = new ManagerImpl();

            // We mab a B obj to a A obj, using the test converter B2AConverver
            mngr.RegisterMapping<ObjectB, ObjectA>("test").UseConverter(new B2AConverver());

            ObjectB sourceB = TestObjectsHolder.GetObjectB();
            ObjectA res = (ObjectA)mngr.ApplyMapping("test", sourceB, null);
            // Test the supposed values
            Assert.IsTrue(res.Chaine == sourceB.BChaine.ToUpper() + "--converted");
            Assert.IsTrue(res.Anid == sourceB.BEntier);
            Assert.IsTrue(res.Doubleobject == sourceB.BDoubleobject);
            Assert.IsTrue(res.Car == 'U'); // the stupid enum conversion case (taking 1st char of enum string)
        }


        /// <summary>
        /// Tests the converter of 1st level (field by field reference copier)
        /// </summary>
        [Test]
        public void TestRoot1StLevelMappingConverters()
        {
            // create manager
            IManager mngr = new ManagerImpl();

            // Copy all props from A to another A
            mngr.RegisterMapping<ObjectA, ObjectA>("propcopier")
                .UseConverter(new PropertyCopierConv()); // all props

            // Copy all value types and string props from A to another A
            mngr.RegisterMapping<ObjectA, ObjectA>("vtpropcopier")
                .UseConverter(new PropertyCopierConv(true)); // value types only

            var source = TestObjectsHolder.GetObjectA();

            ObjectA res1 = (ObjectA) mngr.ApplyMapping("propcopier", source, null);
            ObjectA res2 = (ObjectA) mngr.ApplyMapping("vtpropcopier", source, null);

            // asserts
            Assert.IsTrue(res1.Chaine!=null);
            Assert.IsTrue(res1.Doubleobject != 0);
            Assert.IsTrue(res1.UnObjetB != null);
            Assert.IsTrue(res1.UneListe!= null);

            Assert.IsTrue(res2.Chaine != null);
            Assert.IsTrue(res2.UnObjetB == null);
            Assert.IsTrue(res2.Doubleobject != 0);
            Assert.IsTrue(res2.UneListe == null);

        }

        /// <summary>
        /// Tests the list converter on root object,
        /// with an overriding type because of interface manipulation.
        /// </summary>
        [Test]
        public void TestRootArrayWithOverrideType()
        {
            // create manager
            IManager mngr = new ManagerImpl();

            // Copy all props from A to another A
            mngr.RegisterMapping<List<ObjectA>, IEnumerable<ObjectA>>("rootarraycopier")
                .UseConverter(new ListCrossConv()) // use list converter because we will go to on etype to another
                .OverrideTargetType<HashSet<ObjectA>>(); // the other is a hashset

            List<ObjectA> source=new List<ObjectA>();
            source.Add(TestObjectsHolder.GetObjectA());
            source.Add(TestObjectsHolder.GetObjectA());
            source.Add(TestObjectsHolder.GetObjectA());
            source.Add(TestObjectsHolder.GetObjectA());

            HashSet<ObjectA> tgt = new HashSet<ObjectA>();
            tgt.Add(TestObjectsHolder.GetObjectA());


            // merge
            ICollection<ObjectA> res1 = (ICollection<ObjectA>)mngr
                .ApplyMapping("rootarraycopier", source, tgt);
            Assert.IsTrue(res1.Count==5);

            // new
            ICollection<ObjectA> res2 = (ICollection<ObjectA>)mngr
                .ApplyMapping("rootarraycopier", source);
            Assert.IsTrue(res2.Count == 4);
        }

        /// <summary>
        /// Tests the object to object mapping. (the "itself" mapping)
        /// </summary>
        [Test]
        public void TestObjectToObjectMapping()
        {
            // create manager
            IManager mngr = new ManagerImpl();

            mngr.RegisterMapping<ObjectA, ObjectA>("o2o")
                .GoTo(a => a.UnObjetB, a2 => a2.UnObjetB)
                .Map(s => s, d => d)
                .Map(s => s.BEntier);

            var oa = TestObjectsHolder.GetObjectA();
            oa.UnObjetB.BEntier = 11111;

            var tgt = new ObjectA();
            tgt.UnObjetB = TestObjectsHolder.GetObjectB();
            tgt.UnObjetB.BCar = 'z';

            var res1 = (ObjectA)mngr.ApplyMapping("o2o", oa, tgt);
            Assert.IsTrue(res1!=null);
            Assert.IsTrue(res1.UnObjetB != null); // existing one is kept
            Assert.IsTrue(res1.UnObjetB.BEntier == 11111); // new one is replace

            Assert.IsTrue(res1.UnObjetB.BCar == 'z'); // exisiting one is kept
        }
    }
}
